package spring.cloud.tasks.executor.trigger;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Trigger;
import org.quartz.spi.OperableTrigger;
import spring.cloud.tasks.executor.exception.JobException;
import spring.cloud.tasks.executor.task.TaskExecuteEngine;
import spring.cloud.tasks.executor.utils.log.LogEvents;
import spring.cloud.tasks.executor.utils.log.LogUtils;

import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Setter
@Getter
public class Worker implements Runnable {
    private final Object lock = new Object();
    private final TaskExecuteEngine taskExecuteEngine;
    //
    private final Triggered notTriggered;
    private volatile OperableTrigger operableTrigger;
    private volatile Triggered triggered;
    //

    private AtomicBoolean halted = new AtomicBoolean(false);

    public Worker(TaskExecuteEngine taskExecuteEngine, Triggered notTriggered, Trigger trigger) {
        this.taskExecuteEngine = taskExecuteEngine;
        //
        this.notTriggered = notTriggered;
        this.triggered = notTriggered;
        //
        initTrigger(trigger);
    }

    void reInitTrigger(Trigger trigger) {
        initTrigger(trigger);
        synchronized (lock) {
            lock.notifyAll();
        }
    }

    private void initTrigger(Trigger trigger) {
        if (trigger == null) {
            return;
        }
        if (!(trigger instanceof OperableTrigger)) {
            throw new JobException("the trigger should be the instance of OperableTrigger");
        }
        this.operableTrigger = (OperableTrigger) trigger;
        Date date = this.operableTrigger.computeFirstFireTime(null);
        if (date == null) {
            LogUtils.warn(log, LogEvents.ExecutorEvent.COMMON, "Based on configured schedule, the given trigger {} will never fire.", trigger.getKey(), taskExecuteEngine.getTaskId());
        }
    }

    boolean isShutDown() {
        return halted.get();
    }

    void halt() {
        synchronized (lock) {
            halted.set(true);
            lock.notifyAll();
        }
    }

    void trigger(Triggered triggered) {
        synchronized (lock) {
            this.triggered = triggered == null ? notTriggered : triggered;
            lock.notifyAll();
        }
    }

    Date getNextFireTimePausePeriodEffected() {
        if (operableTrigger == null) {
            return null;
        }
        operableTrigger.updateAfterMisfire(null);
        Date nextFireTime = operableTrigger.getNextFireTime();
        while (nextFireTime != null && taskExecuteEngine.getTaskConfigurationService().isInPausePeriod(nextFireTime)) {
            nextFireTime = operableTrigger.getFireTimeAfter(nextFireTime);
        }
        return nextFireTime;
    }

    @Override
    public void run() {
        while (!halted.get()) {
            try {
                if (halted.get()) {
                    return;
                }
                boolean willReachNextFireTime = false; // 没有下次执行时间，初始化为false
                long timeUntilTrigger = 1000;
                if (operableTrigger != null) {
                    operableTrigger.updateAfterMisfire(null);
                    long now = System.currentTimeMillis();
                    Date nextFireTime = operableTrigger.getNextFireTime();
                    if (nextFireTime != null) {
                        timeUntilTrigger = nextFireTime.getTime() - now;
                        willReachNextFireTime = true;
                    } else {
                        willReachNextFireTime = false;
                    }
                } else {
                    willReachNextFireTime = true;
                }
                if (willReachNextFireTime) {
                    while (operableTrigger != null && timeUntilTrigger > 1000) {
                        if (halted.get()) {
                            return;
                        }
                        synchronized (lock) {
                            if (triggered.isYes()) {
                                break;
                            }
                            //
                            try {
                                lock.wait(timeUntilTrigger);
                            } catch (InterruptedException ignore) {
                            }
                            Date nextFireTime = operableTrigger.getNextFireTime();
                            timeUntilTrigger = nextFireTime.getTime() - System.currentTimeMillis();
                        }
                    }
                }
                if (halted.get()) {
                    return;
                }
                Triggered currentTriggered = notTriggered;
                // 触发执行只有两个条件：1.时间到了 2.点立即执行
                synchronized (lock) {
                    // 重置立即执行标志，赋值当前立即执行数据
                    if (triggered.isYes()) {
                        currentTriggered = triggered;
                        triggered = notTriggered;
                    } else { // 非立即执行。即，执行时间到了，或者没有下次执行时间
                        // 有下次执行时间，即执行时间到了，才执行作业
                        if (operableTrigger != null) {
                            operableTrigger.triggered(null);
                        }
                    }
                }
                taskExecuteEngine.execute(currentTriggered);
            } catch (RuntimeException e) {
                LogUtils.error(log, taskExecuteEngine.getTaskId(), e.getMessage(), e);
            }
        }

    }

}